package org.shadow.shapac;

import java.util.ArrayList;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable;
import android.media.MediaPlayer;
import android.preference.PreferenceManager;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import org.shadow.shapac.DbHelper;
import static android.provider.BaseColumns._ID;
import static org.shadow.shapac.DbConstants.TABLE_NAME;
import static org.shadow.shapac.DbConstants.SCORE;

public class GameView extends View {

	// width and height of the whole maze and width of lines which
	// make the walls
	private int width, height, lineWidth;
	// size of the maze i.e. number of cells in it
	private int mazeSizeX, mazeSizeY;
	// width and height of cells in the maze
	float cellWidth, cellHeight;
	// the following store result of cellWidth+lineWidth
	// and cellHeight+lineWidth respectively
	float totalCellWidth, totalCellHeight;
	// the finishing point of the maze
	private int mazeFinishX, mazeFinishY;
	private Maze maze;
	private Context context;
	private Paint line, ball, background;
	boolean[][] hLines;
	boolean[][] vLines;
	boolean dragging = false;
	boolean[][] nuts = new boolean[][] {
			{ false, true, true, true, true, true, true, true, true, true,
					false },
			{ true, false, true, false, true, true, false, true, false, true,
					false },
			{ true, true, true, true, true, true, true, true, true, true, false },
			{ true, true, true, true, true, true, true, true, true, true, false },
			{ false, false, true, true, true, true, true, true, false, false,
					false },
			{ true, true, true, true, true, true, true, true, true, true, false },
			{ false, false, true, true, true, true, true, true, false, false,
					false },
			{ true, true, true, true, true, true, true, true, true, true, false },
			{ true, true, true, true, true, true, true, true, true, true, false },
			{ true, true, true, true, true, true, true, true, true, true, false },
			{ true, true, true, true, true, true, true, true, true, true, false } };

	private static final int DELAY = 100; // delay between frames in
											// milliseconds

	private ArrayList<Bitmap> mBitmapList = new ArrayList<Bitmap>();
	private ArrayList<Bitmap> nBitmapList = new ArrayList<Bitmap>();
	private ArrayList<Bitmap> pBitmapList = new ArrayList<Bitmap>();

	private int play_frame = 0;
	private int inky_frame = 0;
	private int pinky_frame = 0;
	private long last_tick = 0;
	private long last_tick2 = 0;
	private long last_tick3 = 0;
	private int score = 0;
	private boolean isFinish;

	private String AnimateOrientation = "pac_right";
	private String InkyOrientation = "inky";
	private String PinkyOrientation = "pinky";
	
	private DbHelper dbhelper = null;

	public GameView(Context context) {
		super(context);
		this.context = (Activity) context;

		// get game colors from preferences
		SharedPreferences prefs = PreferenceManager
				.getDefaultSharedPreferences(context);
		int bgColor = getResources().getColor(R.color.game_bg);
		String bgColorStr = prefs.getString("bgColor", "");
		if (bgColorStr.length() > 0) {
			bgColor = Color.parseColor(bgColorStr);
		}
		int lineColor = getResources().getColor(R.color.line);
		String lineColorStr = prefs.getString("wallColor", "");
		if (lineColorStr.length() > 0) {
			lineColor = Color.parseColor(lineColorStr);
		}
		int ballColor = getResources().getColor(R.color.position);
		String ballColorStr = prefs.getString("ballColor", "");
		if (ballColorStr.length() > 0) {
			ballColor = Color.parseColor(ballColorStr);
		}

		line = new Paint();
		line.setColor(lineColor);
		line.setStrokeWidth(10);
		ball = new Paint();
		ball.setColor(ballColor);
		background = new Paint();
		background.setColor(bgColor);
		setFocusable(true);
		this.setFocusableInTouchMode(true);
		
		openDatabase();
	}

	public void setMaze(Maze maze) {
		this.maze = maze;
		mazeFinishX = maze.getFinalX();
		mazeFinishY = maze.getFinalY();
		mazeSizeX = maze.getMazeWidth();
		mazeSizeY = maze.getMazeHeight();
		hLines = maze.getHorizontalLines();
		vLines = maze.getVerticalLines();
	}

	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		width = (w < h) ? w : h;
		height = width; // for now square mazes
		lineWidth = 1; // for now 1 pixel wide walls
		cellWidth = (width - ((float) mazeSizeX * lineWidth)) / mazeSizeX;
		totalCellWidth = cellWidth + lineWidth;
		cellHeight = (height - ((float) mazeSizeY * lineWidth)) / mazeSizeY;
		totalCellHeight = cellHeight + lineWidth;
		ball.setTextSize(cellHeight * 0.75f);
		super.onSizeChanged(w, h, oldw, oldh);
	}

	protected void onDraw(Canvas canvas) {
		// fill in the background
		canvas.drawRect(0, 0, width, height, background);

		// iterate over the boolean arrays to draw walls
		for (int i = 0; i < mazeSizeX; i++) {
			for (int j = 0; j < mazeSizeY; j++) {
				float x = j * totalCellWidth;
				float y = i * totalCellHeight;
				if (j < mazeSizeX - 1 && vLines[i][j]) {
					// we'll draw a vertical line
					canvas.drawLine(x + cellWidth, // start X
							y, // start Y
							x + cellWidth, // stop X
							y + cellHeight, // stop Y
							line);
				}
				if (i < mazeSizeY - 1 && hLines[i][j]) {
					// we'll draw a horizontal line
					canvas.drawLine(x, // startX
							y + cellHeight, // startY
							x + cellWidth, // stopX
							y + cellHeight, // stopY
							line);
				}
			}
		}

		// draw nuts
		if (nuts[maze.getCurrentY()][maze.getCurrentX()]) {
			nuts[maze.getCurrentY()][maze.getCurrentX()] = false;
			score = score + 100;
		}
		for (int i = 0; i < mazeSizeX; i++) {
			for (int j = 0; j < mazeSizeY; j++) {
				float x = j * totalCellWidth;
				float y = i * totalCellHeight;
				if (nuts[i][j]) {

					canvas.drawCircle(x + cellWidth / 2, y + cellHeight / 2,
							(cellWidth * 0.08f), ball);
				}
			}
		}

		int currentX = maze.getCurrentX(), currentY = maze.getCurrentY();
		// draw the ball
		/*
		 * canvas.drawCircle((currentX * totalCellWidth)+(cellWidth/2), //x of
		 * center (currentY * totalCellHeight)+(cellWidth/2), //y of center
		 * (cellWidth*0.45f), //radius ball);
		 */

		// Draw the pacman
		loadPacAnimation(AnimateOrientation, 4);

		long time = (System.currentTimeMillis() - last_tick);
		if (time >= DELAY) // the delay time has passed. set next frame
		{
			last_tick = System.currentTimeMillis();
			canvas.drawBitmap(mBitmapList.get(play_frame),
					(currentX * totalCellWidth) + (cellWidth / 6),
					(currentY * totalCellHeight) + (cellWidth / 6), null);
			play_frame++;
			postInvalidate();
		} else // still within delay. redraw current frame
		{
			canvas.drawBitmap(mBitmapList.get(play_frame),
					(currentX * totalCellWidth) + (cellWidth / 6),
					(currentY * totalCellHeight) + (cellWidth / 6), null);
			postInvalidate();
		}
		if (play_frame > 3)
			play_frame = 0;

		// Draw the enemy Inky
		loadInkyAnimation(InkyOrientation, 2);

		int inkyX = maze.getInkyX(), inkyY = maze.getInkyY();
		long eTime = (System.currentTimeMillis() - last_tick2);
		if (eTime >= 150) // the delay time has passed. set next frame
		{
			last_tick2 = System.currentTimeMillis();
			canvas.drawBitmap(nBitmapList.get(inky_frame), inkyX
					* totalCellWidth, inkyY * totalCellWidth, null);
			inky_frame++;
			postInvalidate();
		} else // still within delay. redraw current frame
		{
			canvas.drawBitmap(nBitmapList.get(inky_frame), inkyX
					* totalCellWidth, inkyY * totalCellWidth, null);
			postInvalidate();
		}
		if (inky_frame > 1)
			inky_frame = 0;
		// Draw the enemy Pinky
		loadPinkyAnimation(PinkyOrientation, 2);

		int pinkyX = maze.getPinkyX(), pinkyY = maze.getPinkyY();
		long pTime = (System.currentTimeMillis() - last_tick3);
		if (pTime >= 150) // the delay time has passed. set next frame
		{
			last_tick3 = System.currentTimeMillis();
			canvas.drawBitmap(pBitmapList.get(pinky_frame), pinkyX
					* totalCellWidth, pinkyY * totalCellWidth, null);
			pinky_frame++;
			postInvalidate();
		} else // still within delay. redraw current frame
		{
			canvas.drawBitmap(pBitmapList.get(pinky_frame), pinkyX
					* totalCellWidth, pinkyY * totalCellWidth, null);
			postInvalidate();
		}
		if (pinky_frame > 1)
			pinky_frame = 0;

		// draw the finishing point indicator
		canvas.drawText("Score: " + score, (totalCellWidth)
				+ (cellWidth * 0.25f), (mazeFinishY * totalCellHeight)
				+ (cellHeight * 2.0f), ball);

	}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent evt) {
		boolean moved = false;
		switch (keyCode) {
		case KeyEvent.KEYCODE_DPAD_UP:
			moved = maze.move(Maze.UP);
			AnimateOrientation = "pac_up";
			maze.inkyMove();
			maze.pinkyMove();
			break;
		case KeyEvent.KEYCODE_DPAD_DOWN:
			moved = maze.move(Maze.DOWN);
			AnimateOrientation = "pac_down";
			maze.inkyMove();
			maze.pinkyMove();
			break;
		case KeyEvent.KEYCODE_DPAD_RIGHT:
			moved = maze.move(Maze.RIGHT);
			AnimateOrientation = "pac_right";
			maze.inkyMove();
			maze.pinkyMove();
			break;
		case KeyEvent.KEYCODE_DPAD_LEFT:
			moved = maze.move(Maze.LEFT);
			AnimateOrientation = "pac_left";
			maze.inkyMove();
			maze.pinkyMove();
			break;
		default:
			return super.onKeyDown(keyCode, evt);
		}
		if (moved) {
			// the ball was moved so we'll redraw the view
			invalidate();
			if (checkGameOver()) {
				showFinishDialog();
			}
			if (maze.isGameOver()) {
				
				MediaPlayer gameOver = MediaPlayer.create(context, R.raw.pacmandies);
				gameOver.start();
				
				showGameoverDialog();
			}
		}
		return true;
	}

	void showFinishDialog() {
		AlertDialog.Builder builder = new AlertDialog.Builder(context);
		builder.setTitle(context.getText(R.string.finished_title));
		LayoutInflater inflater = (LayoutInflater) context
				.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		View view = inflater.inflate(R.layout.finish, null);
		builder.setView(view);
		final AlertDialog finishDialog = builder.create();
		View closeButton = view.findViewById(R.id.closeGame);
		closeButton.setOnClickListener(new OnClickListener() {
			public void onClick(View clicked) {
				if (clicked.getId() == R.id.closeGame) {
					finishDialog.dismiss();
					((Activity) context).finish();
				}
			}
		});
		finishDialog.show();
		
		add();
	}

	void showGameoverDialog() {
		AlertDialog alertDialog = new AlertDialog.Builder(context).create();
		alertDialog.setTitle("GAME OVER!");
		alertDialog.setMessage("Please try again :)");
		alertDialog.setButton("OK", new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int which) {
				// here you can add functions
				((Activity) context).finish();
			}
		});
		alertDialog.setIcon(R.drawable.icon);
		alertDialog.show();
		
		add();
	}

	public boolean onTouchEvent(MotionEvent event) {
		float touchX = event.getX();
		float touchY = event.getY();
		int currentX = maze.getCurrentX();
		int currentY = maze.getCurrentY();
		switch (event.getAction() & MotionEvent.ACTION_MASK) {
		case MotionEvent.ACTION_DOWN:
			// touch gesture started
			if (Math.floor(touchX / totalCellWidth) == currentX
					&& Math.floor(touchY / totalCellHeight) == currentY) {
				// touch gesture in the cell where the ball is
				dragging = true;
				return true;
			}
			break;
		case MotionEvent.ACTION_UP:
			// touch gesture completed
			dragging = false;
			return true;
		case MotionEvent.ACTION_MOVE:
			if (dragging) {
				int cellX = (int) Math.floor(touchX / totalCellWidth);
				int cellY = (int) Math.floor(touchY / totalCellHeight);

				if ((cellX != currentX && cellY == currentY)
						|| (cellY != currentY && cellX == currentX)) {
					// either X or Y changed
					boolean moved = false;
					// check horizontal ball movement
					switch (cellX - currentX) {
					case 1:
						moved = maze.move(Maze.RIGHT);
						maze.inkyMove();
						maze.pinkyMove();
						break;
					case -1:
						moved = maze.move(Maze.LEFT);
						maze.inkyMove();
						maze.pinkyMove();
					}
					// check vertical ball movement
					switch (cellY - currentY) {
					case 1:
						moved = maze.move(Maze.DOWN);
						maze.inkyMove();
						maze.pinkyMove();
						break;
					case -1:
						moved = maze.move(Maze.UP);
						maze.inkyMove();
						maze.pinkyMove();
					}
					if (moved) {

						// the ball was moved so we'll redraw the view
						invalidate();
						if (checkGameOver()) {
							// game is finished
							showFinishDialog();
						}
						if (maze.isGameOver()) {
							
							MediaPlayer gameOver = MediaPlayer.create(context, R.raw.pacmandies);
							gameOver.start();
							
							showGameoverDialog();
						}
					}
				}
				return true;
			}
		}

		return false;
	}

	public void loadPacAnimation(String prefix, int nframes) {
		mBitmapList.clear();

		for (int x = 0; x < nframes; x++) {
			String name = prefix + x;
			int res_id = context.getResources().getIdentifier(name, "drawable",
					context.getPackageName());
			BitmapDrawable d = (BitmapDrawable) context.getResources()
					.getDrawable(res_id);
			mBitmapList.add(d.getBitmap());
		}
	}

	public void loadInkyAnimation(String prefix, int nframes) {
		nBitmapList.clear();

		for (int x = 0; x < nframes; x++) {
			String name = prefix + x;
			int res_id = context.getResources().getIdentifier(name, "drawable",
					context.getPackageName());
			BitmapDrawable d = (BitmapDrawable) context.getResources()
					.getDrawable(res_id);
			nBitmapList.add(d.getBitmap());
		}
	}

	public void loadPinkyAnimation(String prefix, int nframes) {
		pBitmapList.clear();

		for (int x = 0; x < nframes; x++) {
			String name = prefix + x;
			int res_id = context.getResources().getIdentifier(name, "drawable",
					context.getPackageName());
			BitmapDrawable d = (BitmapDrawable) context.getResources()
					.getDrawable(res_id);
			pBitmapList.add(d.getBitmap());
		}
	}
	
    private void add(){
        SQLiteDatabase db = dbhelper.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put(SCORE, Integer.toString(score));
        db.insert(TABLE_NAME, null, values);
    }

    private void openDatabase(){
        dbhelper = new DbHelper(context); 
    }

    private void closeDatabase(){
        dbhelper.close();
    }

	boolean checkGameOver() {
		for (int i = 0; i < mazeSizeX; i++) {
			for (int j = 0; j < mazeSizeY; j++) {
				if (nuts[i][j])
					return false;
			}
		}
		return true;
	}
}
